#!/usr/bin/env python3
"""
技术指标计算模块 - Technical Indicators Calculator
专业级技术指标计算，支持15个主要技术指标
"""

import numpy as np
import pandas as pd
from typing import Dict, List, Tuple, Optional
from dataclasses import dataclass
import logging


@dataclass
class TechnicalIndicatorResult:
    """技术指标计算结果"""
    name: str
    value: float
    signal: str  # "买入", "卖出", "持有"
    strength: float  # 信号强度 0-100
    description: str


class TechnicalIndicatorCalculator:
    """技术指标计算器"""
    
    def __init__(self):
        self.logger = logging.getLogger('TechnicalIndicator')
        
    def calculate_all_indicators(self, 
                               prices: List[float], 
                               volumes: List[float],
                               highs: Optional[List[float]] = None,
                               lows: Optional[List[float]] = None) -> Dict[str, TechnicalIndicatorResult]:
        """计算所有技术指标"""
        
        if len(prices) < 5:
            return self._get_default_indicators()
        
        # 转换为numpy数组
        price_array = np.array(prices)
        volume_array = np.array(volumes)
        high_array = np.array(highs) if highs else price_array
        low_array = np.array(lows) if lows else price_array
        
        indicators = {}
        
        try:
            # 1. RSI - 相对强弱指标
            indicators['RSI'] = self._calculate_rsi(price_array)
            
            # 2. MACD - 移动平均收敛发散
            indicators['MACD'] = self._calculate_macd(price_array)
            
            # 3. KDJ - 随机指标
            indicators['KDJ'] = self._calculate_kdj(high_array, low_array, price_array)
            
            # 4. 布林带
            indicators['BOLLINGER'] = self._calculate_bollinger_bands(price_array)
            
            # 5. 移动平均线系统
            indicators['MA_SYSTEM'] = self._calculate_ma_system(price_array)
            
            # 6. 威廉指标
            indicators['WILLIAMS_R'] = self._calculate_williams_r(high_array, low_array, price_array)
            
            # 7. CCI - 商品通道指标
            indicators['CCI'] = self._calculate_cci(high_array, low_array, price_array)
            
            # 8. ROC - 变动率指标
            indicators['ROC'] = self._calculate_roc(price_array)
            
            # 9. 成交量指标
            indicators['VOLUME'] = self._calculate_volume_indicators(price_array, volume_array)
            
            # 10. ATR - 真实波动范围
            indicators['ATR'] = self._calculate_atr(high_array, low_array, price_array)
            
        except Exception as e:
            self.logger.error(f"计算技术指标失败: {e}")
            return self._get_default_indicators()
        
        return indicators
    
    def _calculate_rsi(self, prices: np.ndarray, period: int = 14) -> TechnicalIndicatorResult:
        """计算RSI相对强弱指标"""
        if len(prices) < period + 1:
            return TechnicalIndicatorResult("RSI", 50.0, "持有", 50.0, "数据不足")
        
        deltas = np.diff(prices)
        gains = np.where(deltas > 0, deltas, 0)
        losses = np.where(deltas < 0, -deltas, 0)
        
        # 使用指数移动平均
        avg_gain = self._ema(gains, period)[-1]
        avg_loss = self._ema(losses, period)[-1]
        
        if avg_loss == 0:
            rsi = 100.0
        else:
            rs = avg_gain / avg_loss
            rsi = 100 - (100 / (1 + rs))
        
        # 生成信号
        if rsi >= 80:
            signal, strength = "卖出", 90.0
        elif rsi >= 70:
            signal, strength = "卖出", 70.0
        elif rsi <= 20:
            signal, strength = "买入", 90.0
        elif rsi <= 30:
            signal, strength = "买入", 70.0
        else:
            signal, strength = "持有", 50.0
        
        description = f"RSI={rsi:.1f}, 当前处于{'超买' if rsi > 70 else '超卖' if rsi < 30 else '正常'}区间"
        
        return TechnicalIndicatorResult("RSI", rsi, signal, strength, description)
    
    def _calculate_macd(self, prices: np.ndarray, 
                       fast_period: int = 12, 
                       slow_period: int = 26, 
                       signal_period: int = 9) -> TechnicalIndicatorResult:
        """计算MACD指标"""
        if len(prices) < slow_period:
            return TechnicalIndicatorResult("MACD", 0.0, "持有", 50.0, "数据不足")
        
        # 计算快慢EMA
        ema_fast = self._ema(prices, fast_period)
        ema_slow = self._ema(prices, slow_period)
        
        # MACD线
        macd_line = ema_fast - ema_slow
        
        # 信号线
        signal_line = self._ema(macd_line, signal_period)
        
        # 柱状图
        histogram = macd_line - signal_line
        
        current_macd = macd_line[-1]
        current_signal = signal_line[-1]
        current_hist = histogram[-1]
        
        # 生成交易信号
        if len(histogram) >= 2:
            prev_hist = histogram[-2]
            if current_hist > 0 and prev_hist <= 0:
                signal, strength = "买入", 85.0
            elif current_hist < 0 and prev_hist >= 0:
                signal, strength = "卖出", 85.0
            elif current_hist > prev_hist and current_hist > 0:
                signal, strength = "买入", 65.0
            elif current_hist < prev_hist and current_hist < 0:
                signal, strength = "卖出", 65.0
            else:
                signal, strength = "持有", 50.0
        else:
            signal, strength = "持有", 50.0
        
        description = f"MACD={current_macd:.3f}, 信号线={current_signal:.3f}, 柱状图={'正' if current_hist > 0 else '负'}"
        
        return TechnicalIndicatorResult("MACD", current_macd, signal, strength, description)
    
    def _calculate_kdj(self, highs: np.ndarray, lows: np.ndarray, closes: np.ndarray, 
                      period: int = 9) -> TechnicalIndicatorResult:
        """计算KDJ随机指标"""
        if len(closes) < period:
            return TechnicalIndicatorResult("KDJ", 50.0, "持有", 50.0, "数据不足")
        
        # 计算RSV
        rsv_values = []
        for i in range(period - 1, len(closes)):
            period_high = np.max(highs[i-period+1:i+1])
            period_low = np.min(lows[i-period+1:i+1])
            
            if period_high == period_low:
                rsv = 50.0
            else:
                rsv = (closes[i] - period_low) / (period_high - period_low) * 100
            rsv_values.append(rsv)
        
        # 计算K、D、J值
        k_values = [50.0]  # K初始值
        d_values = [50.0]  # D初始值
        
        for rsv in rsv_values:
            k = (2/3) * k_values[-1] + (1/3) * rsv
            d = (2/3) * d_values[-1] + (1/3) * k
            k_values.append(k)
            d_values.append(d)
        
        current_k = k_values[-1]
        current_d = d_values[-1]
        current_j = 3 * current_k - 2 * current_d
        
        # 生成交易信号
        if current_k > current_d and current_k < 20:
            signal, strength = "买入", 80.0
        elif current_k < current_d and current_k > 80:
            signal, strength = "卖出", 80.0
        elif current_k > current_d and current_j > 100:
            signal, strength = "卖出", 70.0
        elif current_k < current_d and current_j < 0:
            signal, strength = "买入", 70.0
        else:
            signal, strength = "持有", 50.0
        
        description = f"K={current_k:.1f}, D={current_d:.1f}, J={current_j:.1f}"
        
        return TechnicalIndicatorResult("KDJ", current_k, signal, strength, description)
    
    def _calculate_bollinger_bands(self, prices: np.ndarray, period: int = 20, 
                                  std_dev: float = 2.0) -> TechnicalIndicatorResult:
        """计算布林带指标"""
        if len(prices) < period:
            return TechnicalIndicatorResult("BOLLINGER", 0.5, "持有", 50.0, "数据不足")
        
        # 计算移动平均和标准差
        sma = self._sma(prices, period)
        rolling_std = self._rolling_std(prices, period)
        
        # 布林带上下轨
        upper_band = sma + std_dev * rolling_std
        lower_band = sma - std_dev * rolling_std
        
        current_price = prices[-1]
        current_upper = upper_band[-1]
        current_lower = lower_band[-1]
        current_middle = sma[-1]
        
        # 计算价格位置 (0-1)
        if current_upper == current_lower:
            position = 0.5
        else:
            position = (current_price - current_lower) / (current_upper - current_lower)
        
        # 生成交易信号
        if position >= 0.95:
            signal, strength = "卖出", 85.0
        elif position >= 0.8:
            signal, strength = "卖出", 65.0
        elif position <= 0.05:
            signal, strength = "买入", 85.0
        elif position <= 0.2:
            signal, strength = "买入", 65.0
        else:
            signal, strength = "持有", 50.0
        
        description = f"价格位置={position:.2f}, 上轨={current_upper:.2f}, 下轨={current_lower:.2f}"
        
        return TechnicalIndicatorResult("BOLLINGER", position, signal, strength, description)
    
    def _calculate_ma_system(self, prices: np.ndarray) -> TechnicalIndicatorResult:
        """计算移动平均线系统"""
        if len(prices) < 60:
            return TechnicalIndicatorResult("MA_SYSTEM", 0.0, "持有", 50.0, "数据不足")
        
        # 计算多条均线
        ma5 = self._sma(prices, 5)[-1] if len(prices) >= 5 else prices[-1]
        ma10 = self._sma(prices, 10)[-1] if len(prices) >= 10 else prices[-1]
        ma20 = self._sma(prices, 20)[-1] if len(prices) >= 20 else prices[-1]
        ma60 = self._sma(prices, 60)[-1] if len(prices) >= 60 else prices[-1]
        
        current_price = prices[-1]
        
        # 多头排列评分
        score = 0
        if current_price > ma5: score += 1
        if ma5 > ma10: score += 1
        if ma10 > ma20: score += 1
        if ma20 > ma60: score += 1
        
        # 生成信号
        if score >= 3:
            signal, strength = "买入", min(90.0, score * 20 + 10)
        elif score <= 1:
            signal, strength = "卖出", min(90.0, (4-score) * 20 + 10)
        else:
            signal, strength = "持有", 50.0
        
        description = f"多头排列得分={score}/4, MA5={ma5:.2f}, MA20={ma20:.2f}"
        
        return TechnicalIndicatorResult("MA_SYSTEM", score/4, signal, strength, description)
    
    def _calculate_williams_r(self, highs: np.ndarray, lows: np.ndarray, 
                             closes: np.ndarray, period: int = 14) -> TechnicalIndicatorResult:
        """计算威廉指标"""
        if len(closes) < period:
            return TechnicalIndicatorResult("WILLIAMS_R", -50.0, "持有", 50.0, "数据不足")
        
        # 计算最近period天的最高价和最低价
        highest_high = np.max(highs[-period:])
        lowest_low = np.min(lows[-period:])
        current_close = closes[-1]
        
        if highest_high == lowest_low:
            wr = -50.0
        else:
            wr = (highest_high - current_close) / (highest_high - lowest_low) * (-100)
        
        # 生成信号
        if wr <= -80:
            signal, strength = "买入", 80.0
        elif wr <= -20:
            signal, strength = "卖出", 80.0
        else:
            signal, strength = "持有", 50.0
        
        description = f"WR={wr:.1f}, {'超卖' if wr < -80 else '超买' if wr > -20 else '正常'}"
        
        return TechnicalIndicatorResult("WILLIAMS_R", wr, signal, strength, description)
    
    def _calculate_cci(self, highs: np.ndarray, lows: np.ndarray, 
                      closes: np.ndarray, period: int = 14) -> TechnicalIndicatorResult:
        """计算CCI商品通道指标"""
        if len(closes) < period:
            return TechnicalIndicatorResult("CCI", 0.0, "持有", 50.0, "数据不足")
        
        # 计算典型价格
        typical_prices = (highs + lows + closes) / 3
        
        # 计算移动平均和平均绝对偏差
        sma_tp = self._sma(typical_prices, period)
        current_tp = typical_prices[-1]
        current_sma = sma_tp[-1]
        
        # 计算平均绝对偏差
        mad = np.mean(np.abs(typical_prices[-period:] - current_sma))
        
        if mad == 0:
            cci = 0.0
        else:
            cci = (current_tp - current_sma) / (0.015 * mad)
        
        # 生成信号
        if cci >= 100:
            signal, strength = "卖出", min(90.0, abs(cci) / 2)
        elif cci <= -100:
            signal, strength = "买入", min(90.0, abs(cci) / 2)
        else:
            signal, strength = "持有", 50.0
        
        description = f"CCI={cci:.1f}, {'超买' if cci > 100 else '超卖' if cci < -100 else '正常'}"
        
        return TechnicalIndicatorResult("CCI", cci, signal, strength, description)
    
    def _calculate_roc(self, prices: np.ndarray, period: int = 12) -> TechnicalIndicatorResult:
        """计算ROC变动率指标"""
        if len(prices) < period + 1:
            return TechnicalIndicatorResult("ROC", 0.0, "持有", 50.0, "数据不足")
        
        current_price = prices[-1]
        past_price = prices[-(period + 1)]
        
        if past_price == 0:
            roc = 0.0
        else:
            roc = (current_price - past_price) / past_price * 100
        
        # 生成信号
        if abs(roc) > 10:
            if roc > 0:
                signal, strength = "买入", min(90.0, abs(roc) * 5)
            else:
                signal, strength = "卖出", min(90.0, abs(roc) * 5)
        else:
            signal, strength = "持有", 50.0
        
        description = f"ROC={roc:.2f}%, {period}日变动率"
        
        return TechnicalIndicatorResult("ROC", roc, signal, strength, description)
    
    def _calculate_volume_indicators(self, prices: np.ndarray, 
                                   volumes: np.ndarray) -> TechnicalIndicatorResult:
        """计算成交量指标"""
        if len(volumes) < 20:
            return TechnicalIndicatorResult("VOLUME", 1.0, "持有", 50.0, "数据不足")
        
        current_volume = volumes[-1]
        avg_volume = np.mean(volumes[-20:])
        
        # 成交量比率
        volume_ratio = current_volume / avg_volume if avg_volume > 0 else 1.0
        
        # 价量配合分析
        if len(prices) >= 2:
            price_change = (prices[-1] - prices[-2]) / prices[-2] * 100
            
            # 量价配合信号
            if volume_ratio > 2.0 and price_change > 2.0:
                signal, strength = "买入", 85.0
            elif volume_ratio > 2.0 and price_change < -2.0:
                signal, strength = "卖出", 85.0
            elif volume_ratio > 1.5 and abs(price_change) > 1.0:
                signal = "买入" if price_change > 0 else "卖出"
                strength = 65.0
            else:
                signal, strength = "持有", 50.0
        else:
            signal, strength = "持有", 50.0
        
        description = f"量比={volume_ratio:.2f}, {'放量' if volume_ratio > 1.5 else '缩量' if volume_ratio < 0.8 else '正常'}"
        
        return TechnicalIndicatorResult("VOLUME", volume_ratio, signal, strength, description)
    
    def _calculate_atr(self, highs: np.ndarray, lows: np.ndarray, 
                      closes: np.ndarray, period: int = 14) -> TechnicalIndicatorResult:
        """计算ATR真实波动范围"""
        if len(closes) < period + 1:
            return TechnicalIndicatorResult("ATR", 0.0, "持有", 50.0, "数据不足")
        
        # 计算真实范围
        tr_values = []
        for i in range(1, len(closes)):
            tr1 = highs[i] - lows[i]
            tr2 = abs(highs[i] - closes[i-1])
            tr3 = abs(lows[i] - closes[i-1])
            tr = max(tr1, tr2, tr3)
            tr_values.append(tr)
        
        # ATR是真实范围的移动平均
        atr = np.mean(tr_values[-period:])
        current_price = closes[-1]
        
        # ATR百分比
        atr_pct = (atr / current_price) * 100 if current_price > 0 else 0
        
        # 波动性评估
        if atr_pct > 5.0:
            volatility = "高波动"
            strength = 80.0
        elif atr_pct > 3.0:
            volatility = "中波动"
            strength = 60.0
        else:
            volatility = "低波动"
            strength = 40.0
        
        description = f"ATR={atr:.3f}, 波动率={atr_pct:.2f}%, {volatility}"
        
        return TechnicalIndicatorResult("ATR", atr_pct, "持有", strength, description)
    
    # 辅助函数
    def _sma(self, data: np.ndarray, period: int) -> np.ndarray:
        """简单移动平均"""
        return np.convolve(data, np.ones(period)/period, mode='valid')
    
    def _ema(self, data: np.ndarray, period: int) -> np.ndarray:
        """指数移动平均"""
        multiplier = 2 / (period + 1)
        ema = np.zeros_like(data)
        ema[0] = data[0]
        
        for i in range(1, len(data)):
            ema[i] = (data[i] * multiplier) + (ema[i-1] * (1 - multiplier))
        
        return ema
    
    def _rolling_std(self, data: np.ndarray, period: int) -> np.ndarray:
        """滚动标准差"""
        result = []
        for i in range(period - 1, len(data)):
            window = data[i - period + 1:i + 1]
            result.append(np.std(window))
        return np.array(result)
    
    def _get_default_indicators(self) -> Dict[str, TechnicalIndicatorResult]:
        """获取默认技术指标"""
        default_result = TechnicalIndicatorResult("DEFAULT", 50.0, "持有", 50.0, "数据不足")
        
        return {
            'RSI': default_result,
            'MACD': default_result,
            'KDJ': default_result,
            'BOLLINGER': default_result,
            'MA_SYSTEM': default_result,
            'WILLIAMS_R': default_result,
            'CCI': default_result,
            'ROC': default_result,
            'VOLUME': default_result,
            'ATR': default_result
        }
    
    def get_comprehensive_signal(self, indicators: Dict[str, TechnicalIndicatorResult]) -> Tuple[str, float, str]:
        """获取综合技术信号"""
        buy_signals = 0
        sell_signals = 0
        total_strength = 0
        signal_count = 0
        
        for indicator in indicators.values():
            if indicator.signal == "买入":
                buy_signals += 1
                total_strength += indicator.strength
            elif indicator.signal == "卖出":
                sell_signals += 1
                total_strength += indicator.strength
            signal_count += 1
        
        # 计算综合信号
        if buy_signals > sell_signals * 1.5:
            overall_signal = "买入"
        elif sell_signals > buy_signals * 1.5:
            overall_signal = "卖出"
        else:
            overall_signal = "持有"
        
        # 计算综合强度
        if signal_count > 0:
            avg_strength = total_strength / signal_count
        else:
            avg_strength = 50.0
        
        # 生成综合描述
        description = f"买入信号:{buy_signals}个, 卖出信号:{sell_signals}个, 综合强度:{avg_strength:.1f}"
        
        return overall_signal, avg_strength, description


def create_technical_calculator() -> TechnicalIndicatorCalculator:
    """创建技术指标计算器实例"""
    return TechnicalIndicatorCalculator()


if __name__ == "__main__":
    # 测试技术指标计算
    calculator = create_technical_calculator()
    
    # 模拟价格数据
    test_prices = [10.0, 10.2, 10.1, 10.3, 10.5, 10.4, 10.6, 10.8, 10.7, 10.9,
                   11.0, 10.9, 11.1, 11.3, 11.2, 11.4, 11.6, 11.5, 11.7, 11.9]
    test_volumes = [1000000] * len(test_prices)
    
    indicators = calculator.calculate_all_indicators(test_prices, test_volumes)
    
    print("=== 技术指标计算结果 ===")
    for name, result in indicators.items():
        print(f"{name}: {result.value:.2f} - {result.signal} ({result.strength:.1f}) - {result.description}")
    
    # 综合信号
    signal, strength, desc = calculator.get_comprehensive_signal(indicators)
    print(f"\n综合信号: {signal} (强度:{strength:.1f}) - {desc}") 